#pragma once

#include <ddblk/apds/combine_int.h>
#include <dbg.h>

/* 标准combing, 主要思想是降低通信开销瓶颈 */
/* 使用线程数组 */
/* 批量完成线程 */
/* 批量通知线程降低通讯开销 */
/* 不需要节点还是? */
typedef struct _scmbnnode{
	struct{
		APDSFUTURE_DECL
		tid_t tid;
	} CACHE_ALIGNED;
} _scmbnnode_t;

typedef struct _scmbn {
	struct{
		lkword_t lock;
		tid_t	cur_helpedtid;
	} CACHE_ALIGNED;
	_scmbnnode_t node[APDS_MAX_THREADS];
} PAGE_ALIGNED _scmbn_t;


DECL_COMBINE(scmbn)

SAI void __scmbn_init(_scmbn_t *s)
{
	s->lock = 0;
	s->cur_helpedtid = 0;
}
SAI void *__scmbn_destruct(_scmbn_t *s){
	return (void *)s;
}
/* 原子加原子减开销太大，是否能用ffwd那种更简单的通知机制? */
/* 提交请求 通知远程, 完成请求通知线程 */
SAI _scmbnnode_t *__scmbn_fetch(_scmbn_t *s, apdsfunc_t *pfunc, apdsarg_t *args, apdstinfo_t *tinfo){
	tid_t tid = tinfo->tid;
	_scmbnnode_t *node = &s->node[tid];
	/* 参数入队 */
	apds_movargs(&node->args, args);
	/* 通知 */
	node->tid = tid;
	atm_strel(&node->func, pfunc);

	return node;
}

SAI apdsret_t __scmbn_wait(_scmbn_t *s, _scmbnnode_t *node, INFUN(cmbn_wait, waitpolicy)){
	waitcnt_t cnt = -1;
	lkword_t locked = 1;
	lkword_t unlocked = 0;
	lkword_t lock;
	void *pending;
	lock = atm_ld(&s->lock);
	do {
		pending = atm_ld(&node->func);
		while (lock && pending)
		{
			cnt = waitpolicy->waitf(&s->lock, &locked, cnt);
			lock = atm_ld(&s->lock);
			pending = atm_ld(&node->func);
		}
		if (! pending){
			mp_rmb(); /* 同一cache行且存在读依赖 */
			return apds_args_ret(&node->args);
		}
		if(!lock){
			if (cas_acq_s(&s->lock, &lock, 1))
				break;
		}
	} while(TRUE);


	/* word_t help_count = 0; */
	tid_t start_tid = node->tid;
	/* 清零后几位 */
	tid_t cur_tid = start_tid;
	tid_t next_tid;
	/* 帮助终止条件是一轮 */
	_scmbnnode_t *cur;

	extern cid_t __max_threads;
	tid_t max_tid = atm_ld(&__max_threads);
	
	do
	{
		/* 先判断 */
		// for (word_t idx = 0; idx < WORD_WIDTH; idx++){
		/* next_tid = (cur_tid + 1) & (APDS_MAX_THREADS - 1); */
		next_tid = (cur_tid + 1) % max_tid;
		cur = &s->node[cur_tid];
		/* APDS_PREFETCH(&s->node[next_tid]); */
		apdsfunc_t *func = cur->func;
		if (func)
		{
			apds_set_ret(&cur->args, apds_sync_single(func, &cur->args));
			/* 通知完成 */
			atm_strel(&cur->func, NULL);
			/* help_count++; */
		}
		cur_tid = next_tid;
	} while(cur_tid != start_tid);

	atm_strel(&s->lock, 0);
	
	waitpolicy->wakef(&s->lock, &unlocked);
	return apds_args_ret(&node->args);
}

PARAM_NONULL(1) SAI _scmbn_t *_scmbn_init(void *ptr, size_t size, INFUN(cmbn_node, nodemgr))
{
	PARAM_USED(nodemgr);
	if (size < sizeof(_scmbn_t)) return NULL;
	_scmbn_t *f = (_scmbn_t *)ptr;
	__scmbn_init(f);
	return f;
}
PARAM_NONULL(1) SAI void *_scmbn_destruct(_scmbn_t *f, INFUN(cmbn_node, nodemgr)){
	PARAM_USED(nodemgr);
	void *rptr = __scmbn_destruct(f);
	return rptr;
}
PARAM_NONULL(1, 2, 3) SAI _scmbnnode_t *_scmbn_fetch(_scmbn_t *f, apdsfunc_t *pfunc, apdsarg_t *args, apdstinfo_t *tinfo, INFUN(cmbn_node, nodemgr)){
	PARAM_USED(tinfo,nodemgr);
	return __scmbn_fetch(f, pfunc, args, tinfo);
}
PARAM_NONULL(1, 2) SAI apdsret_t _scmbn_wait(_scmbn_t *f, _scmbnnode_t *node, apdstinfo_t *tinfo, INFUN(cmbn_wait, waitpolicy), INFUN(cmbn_node, nodemgr)){
	PARAM_USED(tinfo, nodemgr);
	apdsret_t ret = __scmbn_wait(f, node, waitpolicy);
	return ret;
}
